package cn.ultrarisk.scadmin.manager.util;

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * JsonUtil
 *
 * @author sevendlong(lin23871@163.com)
 * @date 2014/5/8
 * @since V1.0
 */
public class JsonUtil {
    private static class JsonListDeserializer implements JsonDeserializer<List<Object>> {
        @Override
        public List<Object> deserialize(JsonElement jsonElement, Type type,
                                        JsonDeserializationContext context) throws JsonParseException {
            List<Object> result = new ArrayList<Object>();
            JsonArray jsonArray = jsonElement.getAsJsonArray();
            Iterator<JsonElement> iter = jsonArray.iterator();
            while (iter.hasNext()) {
                JsonElement arrElement = iter.next();
                if (!arrElement.isJsonNull()) {
                    if (arrElement.isJsonPrimitive()) {
                        result.add(arrElement.getAsString());
                    } else if (arrElement.isJsonArray()) {
                        result.add(context.deserialize(arrElement, type));
                    } else if (arrElement.isJsonObject()) {
                        Type mapType = new TypeToken<Map<String, Object>>() {
                        }.getType();
                        result.add(context.deserialize(arrElement, mapType));
                    } else {
                        throw new JsonParseException("some meaningful message");
                    }
                }
            }
            return result;
        }
    }

    private static class JsonMapDeserializer implements JsonDeserializer<Map<String, Object>> {
        @Override
        public Map<String, Object> deserialize(JsonElement jsonElement, Type mapType,
                                               JsonDeserializationContext context) throws JsonParseException {
            if (!jsonElement.isJsonObject()) {
                throw new JsonParseException("some meaningful message");
            }
            Map<String, Object> result = new HashMap<String, Object>();
            JsonObject jsonObject = jsonElement.getAsJsonObject();
            for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
                String key = entry.getKey();
                JsonElement element = entry.getValue();
                if (!element.isJsonNull()) {
                    if (element.isJsonPrimitive()) {
                        result.put(key, element.getAsString());
                    } else if (element.isJsonArray()) {
                        Type listType = new TypeToken<List<Object>>() {
                        }.getType();
                        result.put(key, context.deserialize(element, listType));
                    } else if (element.isJsonObject()) {
                        result.put(key, context.deserialize(element, mapType));
                    } else {
                        throw new JsonParseException("some meaningful message");
                    }
                }
            }
            return result;
        }
    }

    public static class MapWrapper extends HashMap<String, Object> {
        private static final long serialVersionUID = -499489552009668233L;

        @SuppressWarnings("unchecked")
        public MapWrapper(Object m) {
            this((Map<String, Object>) m);
        }

        public MapWrapper(Map<String, Object> m) {
            this.putAll(m);
        }

        @SuppressWarnings("unchecked")
        public Object getByPath(String... paths) {
            String path = CollectionUtil.join(paths, ".");
            String[] keys = path.split("\\.");

            Pattern pattern = Pattern.compile("(\\S+)\\[(\\d+)\\]");
            Object result = this;
            int i = 0;
            while (i < keys.length) {
                if (result == null) {
                    return null;
                }
                Matcher matcher = pattern.matcher(keys[i]);
                if (matcher.matches()) {
                    String key = matcher.group(1);
                    int idx = Integer.parseInt(matcher.group(2));
                    List<Object> list = ((List<Object>) (((Map<String, Object>) result).get(key)));
                    result = (list != null && list.size() > idx) ? list.get(idx) : null;
                } else {
                    result = ((Map<String, Object>) result).get(keys[i]);
                }
                ++i;
            }
            return result;
        }

        public String getStringByPath(String... paths) {
            Object result = getByPath(paths);
            return result != null ? result.toString() : null;
        }

        public Integer getIntByPath(String... paths) {
            Object result = getByPath(paths);
            return result != null ? Integer.parseInt(result.toString()) : null;
        }

        public Long getLongByPath(String... paths) {
            Object result = getByPath(paths);
            return result != null ? Long.parseLong(result.toString()) : null;
        }

        public Double getDoubleByPath(String... paths) {
            Object result = getByPath(paths);
            return result != null ? Double.parseDouble(result.toString()) : null;
        }

        public Boolean getBooleanByPath(String... paths) {
            Object result = getByPath(paths);
            return result != null && Boolean.parseBoolean(result.toString());
        }

        /**
         * 获取格林威治时间 (GMT)
         *
         * @query paths
         * @return
         */
        public Date getGMTDateByPath(String... paths) {
            String result = getStringByPath(paths);
            if (result != null) {
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
                format.setTimeZone(TimeZone.getTimeZone("GMT"));
                try {
                    return format.parse(result);
                } catch (ParseException ignored) {
                }
            }
            return null;
        }

        @SuppressWarnings("unchecked")
        public MapWrapper getMapWrapperByPath(String... paths) {
            Object result = getByPath(paths);
            if (result != null) {
                return new MapWrapper((Map<String, Object>) result);
            }
            return null;
        }
    }

    public static List<Object> fromJsonAsList(String s) {
        Type mapType = new TypeToken<Map<String, Object>>() {
        }.getType();
        Type listType = new TypeToken<List<Object>>() {
        }.getType();
        GsonBuilder gsonBuilder = new GsonBuilder();
        try {
            gsonBuilder.registerTypeAdapter(mapType, new JsonMapDeserializer());
            gsonBuilder.registerTypeAdapter(listType, new JsonListDeserializer());
            Gson gson = gsonBuilder.create();
            return gson.fromJson(s, listType);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static Map<String, Object> fromJsonAsMap(String s) {
        Type mapType = new TypeToken<Map<String, Object>>() {
        }.getType();
        Type listType = new TypeToken<List<Object>>() {
        }.getType();
        GsonBuilder gsonBuilder = new GsonBuilder();
        try {
            gsonBuilder.registerTypeAdapter(mapType, new JsonMapDeserializer());
            gsonBuilder.registerTypeAdapter(listType, new JsonListDeserializer());
            Gson gson = gsonBuilder.create();
            return gson.fromJson(s, mapType);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将字符串转换成T类型的对象
     *
     * @query <T>
     * @query s
     * @query c
     * @return
     */
    public static <T> T fromJson(String s, Class<T> c) {
        Gson gson = new Gson();
        try {
            return gson.fromJson(s, c);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将s转换成type类型的对象，主要用于泛型对象转换，比如List<T>
     *
     * @query <T>
     * @query s
     * @query type 例子：Type type = new TypeToken<List<Long>>(){}.getType();
     * @return
     */
    public static <T> T fromJson(String s, Type type) {
        Gson gson = new Gson();
        try {
            return gson.fromJson(s, type);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 直接转换成json字符串，不包含为空字段，
     *
     * @query o
     * @return
     */
    public static String toJson(Object o) {
        return toJson(o, false);
    }

    /**
     * 转换成json字符串
     * withNullField为true是包含空字段，withNullField为false时不包含空字段
     *
     * @query o
     * @query withNullField 是否包含空字段
     * @return
     */
    public static String toJson(Object o, boolean withNullField) {
        return toJson(o, withNullField, false);
    }

    /**
     * 转换成json字符串
     * withNullField为true是包含空字段，withNullField为false时不包含空字段
     * withoutExposeAnnotation为true时排除@Expose注释的字段，withoutExposeAnnotation为false是不排除@Expose注释的字段
     *
     * @query o
     * @query withNullField
     * @query withoutExposeAnnotation
     * @return
     */
    public static String toJson(Object o, boolean withNullField, boolean withoutExposeAnnotation) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        if (withNullField) {
            gsonBuilder.serializeNulls();
        }
        if (withoutExposeAnnotation) {
            gsonBuilder.excludeFieldsWithoutExposeAnnotation();
        }
        Gson gson = gsonBuilder.create();
        return gson.toJson(o);
    }

}
